#!/usr/bin/python
# Copyright 2007 Alex K (wtwf.com) All rights reserved.

"""Display data about picasaweb album sizes.

Usage: %(PROGRAM)s
	-u [username]	--user [username] - picasaweb username
	-e [email]	--email [email] - google account to log in (optional)
        -p [password]	--password [password] - password for google account
	-f [filename]	--file [filename] - load xml file (debug mostly)
"""

try:
  from xml.etree import ElementTree # for Python 2.5 users
except:
  from elementtree import ElementTree

import getopt
import getpass
import httplib
import logging
import os
import sys
import urllib
import urllib2

def usage(code=True, msg=''):
  if code:
    fd = sys.stderr
  else:
    fd = sys.stdout
  PROGRAM = os.path.basename(sys.argv[0])
  print >> fd, __doc__ % locals()
  if msg:
    print >> fd, msg
  sys.exit(code)

class AlbumSize:

  def __init__(self, email, password, user):
    """
    """
    self.user = user
    self.auth_cookie = None
    if email:
      request_body = urllib.urlencode({'Email': email,
                                       'Passwd': password,
                                       'accountType': 'GOOGLE',
                                       'service': 'xapi',
                                       'source': 'arkpicasaweb'})

      # Open a connection to the authentication server.
      auth_connection = httplib.HTTPSConnection('www.google.com')

      # Begin the POST request to the client login service.
      auth_connection.putrequest('POST', '/accounts/ClientLogin')
      # Set the required headers for an Account Authentication request.
      auth_connection.putheader('Content-type',
                                'application/x-www-form-urlencoded')
      auth_connection.putheader('Content-Length', str(len(request_body)))
      auth_connection.endheaders()

      logging.info("Authenticating")
      auth_connection.send(request_body)

      # Process the response and throw exceptions if the login request did not
      # succeed.
      auth_response = auth_connection.getresponse()
      if auth_response.status == 200:
        response_body = auth_response.read()
        cookies = response_body.splitlines()
      else:
        print "Something failed"
        sys.exit()
      self.auth_cookie = cookies[0]

  def GetSizes(self):
    """
    """
    # now send the request

    req = urllib.urlencode({'kind': 'album'})
    user = self.user

    url = 'http://picasaweb.google.com/data/feed/api/user/%s?%s' % (user, req)
    req = urllib2.Request(url=url)

    if self.auth_cookie:
      req.add_header('Cookie', self.auth_cookie)
    logging.info("Requesting data for: %s", user)
    f = urllib2.urlopen(req)
    txt = f.read()

    ProcessAlbumsFeed(txt)

def NameSpaceElement(namespace, name):
  """Get an element name for ElementTree.findall() that uses a namespace."""
  return ".//{%s}%s" % (namespace, name)

def ProcessAlbumsFeed(txt):
  """Process the text of an album feed.
  Either from a file or a http request."""

  atomnamespace = "http://www.w3.org/2005/Atom"
  gphotonamespace = "http://schemas.google.com/photos/2007"

  tree = ElementTree.fromstring(txt)

  # Create an array of albums. each entry is just a simple list of
  # album properties (including name)
  entries = tree.findall(NameSpaceElement(atomnamespace, "entry"))
  albums = []
  for entry in entries:
    title = entry.findtext(NameSpaceElement(atomnamespace, "title"))
    num = int(entry.findtext(NameSpaceElement(gphotonamespace, "numphotos")))
    size = entry.findtext(NameSpaceElement(gphotonamespace, "bytesUsed"))
    if size:
      size = int(size) / 1000
    else:
      size = 0
    density = 0
    if size > 0:
      density = (num * 10000) / size
    albums.append([title, num, size, density])

  # Add a column to each album, its position in order of biggest albums.
  albums.sort(lambda x, y: cmp(y[2], x[2]))
  for x in range(len(albums)):
    albums[x].append(x)
  # Add another column in order of density.
  albums.sort(lambda x, y: cmp(x[3], y[3]))
  for x in range(len(albums)):
    albums[x].append(x)
  # Now work out the best albums to trim down (from size & density positions)
  for x in range(len(albums)):
    albums[x].append(albums[x][-1] + albums[x][-2])
  # Now sort on that new, last column.
  albums.sort(lambda x, y: cmp(x[-1], y[-1]))

  fmt = "%27s pics:%3d size:%5d density:%5d size#:%2d density#:%2d target:%2d"
  print "\n".join([fmt % tuple(x) for x in albums])


def main():
  """The main function runs the Bookmarks application with the provided
  username and password values.  Authentication credentials are required.
  NOTE:  It is recommended that you run this sample using a test account."""

  logging.basicConfig()
  logging.getLogger().setLevel(logging.DEBUG)
  logging.debug("Starting %s", " ".join(sys.argv))

  # parse command line options
  try:
    opts, args = getopt.getopt(sys.argv[1:], "e:p:u:f:", ["email=", "password=",
                                                          "user=", "file="])
  except getopt.error, msg:
    usage(msg=msg)

  # Process options
  filename = None
  password = None
  email = None
  user = None
  for opt, arg in opts:
    if opt in ("-f", "--file"):
      filename = arg
    if opt in ("-e", "--email"):
      email = arg
    if opt in ("-p", "--password"):
      password = arg
    if opt in ("-u", "--user"):
      user = arg

  if filename:
    ProcessAlbumsFeed(open(filename).read())
    return

  if email and not password:
    password = getpass.getpass("Enter password for '%s': " % email)

  if not user:
    usage(msg="You must provide a username")
  als = AlbumSize(email, password, user)
  als.GetSizes()

if __name__ == '__main__':
  main()
